Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / androidApp / src / google / kotlin / org / meshtastic / app / map / component / MarkerBitmapRenderer.kt

Displaying Raw • Download

androidApp/src/google/kotlin/org/meshtastic/app/map/component/MarkerBitmapRenderer.kt 0f123adb72d3d13a208959a17f7d9dd40809804b (0f123adb) Text, 6.70 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.app.map.component

Tff7b72import T7ee787android.content.Context
Tff7b72import T7ee787android.graphics.Canvas
Tff7b72import T7ee787android.graphics.Paint
Tff7b72import T7ee787android.graphics.RectF
Tff7b72import T7ee787android.graphics.Typeface
Tff7b72import T7ee787android.text.TextPaint
Tff7b72import T7ee787androidx.compose.runtime.Composable
Tff7b72import T7ee787androidx.compose.runtime.remember
Tff7b72import T7ee787androidx.compose.ui.platform.LocalContext
Tff7b72import T7ee787androidx.compose.ui.platform.LocalDensity
Tff7b72import T7ee787androidx.core.graphics.createBitmap
Tff7b72import T7ee787com.google.android.gms.maps.model.BitmapDescriptor
Tff7b72import T7ee787com.google.android.gms.maps.model.BitmapDescriptorFactory
Tff7b72import T7ee787org.meshtastic.app.map.MapsSdkInitializer
Tff7b72import T7ee787org.meshtastic.core.model.Node

Tff7b72private Tff7b72const Tff7b72val Te6edf3CHIP_CORNER_RADIUS_DP Tff7b72= T79c0ff4f
Tff7b72private Tff7b72const Tff7b72val Te6edf3CHIP_PADDING_HORIZONTAL_DP Tff7b72= T79c0ff8f
Tff7b72private Tff7b72const Tff7b72val Te6edf3CHIP_MIN_WIDTH_DP Tff7b72= T79c0ff6T79c0ff4f
Tff7b72private Tff7b72const Tff7b72val Te6edf3CHIP_MIN_HEIGHT_DP Tff7b72= T79c0ff2T79c0ff8f
Tff7b72private Tff7b72const Tff7b72val Te6edf3CHIP_TEXT_SIZE_SP Tff7b72= T79c0ff1T79c0ff4f
Tff7b72private Tff7b72const Tff7b72val Te6edf3EMOJI_TEXT_SIZE_SP Tff7b72= T79c0ff3T79c0ff2f
Tff7b72private Tff7b72const Tff7b72val Te6edf3EMOJI_PADDING_DP Tff7b72= T79c0ff2f

T8b949e/**
* Renders a node chip marker as a [BitmapDescriptor] using Canvas — avoids the off-screen ComposeView pipeline in
* maps-compose's `MarkerComposable`/`rememberComposeBitmapDescriptor` which can crash with "The ComposeView was
* measured to have a width or height of zero" during subcomposition races (googlemaps/android-maps-compose#875).
*/
Tf0883e@Composable
Tff7b72fun Td2a8ffrememberNodeChipDescriptorTb4b4b4(Te6edf3nodeTb4b4b4: Te6edf3NodeTb4b4b4)Tb4b4b4: Te6edf3BitmapDescriptor Tb4b4b4{
Tff7b72val Te6edf3context Tff7b72= Te6edf3LocalContextTb4b4b4.Te6edf3current
Tff7b72val Te6edf3density Tff7b72= Te6edf3LocalDensityTb4b4b4.Te6edf3currentTb4b4b4.Te6edf3density
Tff7b72val Te6edf3fontScale Tff7b72= Te6edf3LocalDensityTb4b4b4.Te6edf3currentTb4b4b4.Te6edf3fontScale
Tff7b72return Te6edf3rememberTb4b4b4(Te6edf3nodeTb4b4b4.Te6edf3numTb4b4b4, Te6edf3nodeTb4b4b4.Te6edf3userTb4b4b4.Te6edf3short_nameTb4b4b4, Te6edf3nodeTb4b4b4.Te6edf3colorsTb4b4b4, Te6edf3nodeTb4b4b4.Te6edf3isIgnoredTb4b4b4) Tb4b4b4{
Te6edf3buildNodeChipDescriptorTb4b4b4(Te6edf3contextTb4b4b4, Te6edf3nodeTb4b4b4, Te6edf3densityTb4b4b4, Te6edf3fontScaleTb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e/**
* Non-`@Composable` variant of [rememberNodeChipDescriptor] for callers that have no composition to read
* [LocalContext]/[LocalDensity] from — specifically a [com.google.maps.android.clustering.view.DefaultClusterRenderer]
* building marker icons on its background render thread. Keeping the cluster icon on this Canvas path (instead of
* maps-compose's `clusterItemContent` Composable) avoids the off-screen ComposeView in `ComposeUiClusterRenderer`,
* which has no reachable `ViewTreeLifecycleOwner` from the async render Handler and was our top FATAL
* (googlemaps/android-maps-compose#325/#875).
*/
Tff7b72fun Td2a8ffbuildNodeChipDescriptorTb4b4b4(Te6edf3contextTb4b4b4: Te6edf3ContextTb4b4b4, Te6edf3nodeTb4b4b4: Te6edf3NodeTb4b4b4, Te6edf3densityTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3fontScaleTb4b4b4: Tffa657FloatTb4b4b4)Tb4b4b4: Te6edf3BitmapDescriptor Tb4b4b4{
Te6edf3ensureMapsInitializedTb4b4b4(Te6edf3contextTb4b4b4)
Tff7b72return Te6edf3renderNodeChipBitmapTb4b4b4(Te6edf3nodeTb4b4b4, Te6edf3densityTb4b4b4, Te6edf3fontScaleTb4b4b4)
Tb4b4b4}

T8b949e/** Renders an emoji waypoint marker as a [BitmapDescriptor] using Canvas. */
Tf0883e@Composable
Tff7b72fun Td2a8ffrememberEmojiMarkerDescriptorTb4b4b4(Te6edf3codePointTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3BitmapDescriptor Tb4b4b4{
Tff7b72val Te6edf3context Tff7b72= Te6edf3LocalContextTb4b4b4.Te6edf3current
Tff7b72val Te6edf3density Tff7b72= Te6edf3LocalDensityTb4b4b4.Te6edf3currentTb4b4b4.Te6edf3density
Tff7b72val Te6edf3fontScale Tff7b72= Te6edf3LocalDensityTb4b4b4.Te6edf3currentTb4b4b4.Te6edf3fontScale
Tff7b72return Te6edf3rememberTb4b4b4(Te6edf3codePointTb4b4b4) Tb4b4b4{
Te6edf3ensureMapsInitializedTb4b4b4(Te6edf3contextTb4b4b4)
Te6edf3renderEmojiBitmapTb4b4b4(Te6edf3codePointTb4b4b4, Te6edf3densityTb4b4b4, Te6edf3fontScaleTb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e/**
* [BitmapDescriptorFactory] only works after the Maps SDK has been initialized, which normally happens when a
* GoogleMap/MapView is created. These descriptors are built during composition, and on the node-detail inline map the
* icon is computed before that screen's GoogleMap has loaded the SDK — so [BitmapDescriptorFactory.fromBitmap] crashes
* with "IBitmapDescriptorFactory is not initialized". Delegate to [MapsSdkInitializer], which initializes the SDK
* synchronously and idempotently (and reports the loaded renderer), so this is a no-op once the SDK is already up.
*/
Tff7b72private Tff7b72fun Td2a8ffensureMapsInitializedTb4b4b4(Te6edf3contextTb4b4b4: Te6edf3ContextTb4b4b4) Tb4b4b4{
Te6edf3MapsSdkInitializerTb4b4b4.Te6edf3ensureInitializedTb4b4b4(Te6edf3contextTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffrenderNodeChipBitmapTb4b4b4(Te6edf3nodeTb4b4b4: Te6edf3NodeTb4b4b4, Te6edf3densityTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3fontScaleTb4b4b4: Tffa657FloatTb4b4b4)Tb4b4b4: Te6edf3BitmapDescriptor Tb4b4b4{
Tff7b72val Tb4b4b4(Te6edf3textColorIntTb4b4b4, Te6edf3nodeColorIntTb4b4b4) Tff7b72= Te6edf3nodeTb4b4b4.Te6edf3colors
Tff7b72val Te6edf3scaledDensity Tff7b72= Te6edf3density Tff7b72* Te6edf3fontScale

Tff7b72val Te6edf3textPaint Tff7b72=
Te6edf3TextPaintTb4b4b4(Te6edf3PaintTb4b4b4.Te6edf3ANTI_ALIAS_FLAGTb4b4b4)Tb4b4b4.Te6edf3apply Tb4b4b4{
Te6edf3textSize Tff7b72= Te6edf3CHIP_TEXT_SIZE_SP Tff7b72* Te6edf3scaledDensity
Te6edf3typeface Tff7b72= Te6edf3TypefaceTb4b4b4.Te6edf3DEFAULT_BOLD
Te6edf3color Tff7b72= Te6edf3textColorInt
Te6edf3textAlign Tff7b72= Te6edf3PaintTb4b4b4.Te6edf3AlignTb4b4b4.Te6edf3CENTER
Te6edf3isStrikeThruText Tff7b72= Te6edf3nodeTb4b4b4.Te6edf3isIgnored
Tb4b4b4}
Tff7b72val Te6edf3label Tff7b72= Te6edf3nodeTb4b4b4.Te6edf3userTb4b4b4.Te6edf3short_nameTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Ta5d6ff"Ta5d6ff???Ta5d6ff" Tb4b4b4}

Tff7b72val Te6edf3textWidth Tff7b72= Te6edf3textPaintTb4b4b4.Te6edf3measureTextTb4b4b4(Te6edf3labelTb4b4b4)
Tff7b72val Te6edf3paddingH Tff7b72= Te6edf3CHIP_PADDING_HORIZONTAL_DP Tff7b72* Te6edf3density
Tff7b72val Te6edf3minWidth Tff7b72= Te6edf3CHIP_MIN_WIDTH_DP Tff7b72* Te6edf3density
Tff7b72val Te6edf3minHeight Tff7b72= Te6edf3CHIP_MIN_HEIGHT_DP Tff7b72* Te6edf3density

Tff7b72val Te6edf3width Tff7b72= Te6edf3maxOfTb4b4b4(Te6edf3minWidthTb4b4b4, Te6edf3textWidth Tff7b72+ Te6edf3paddingH Tff7b72* T79c0ff2Tb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3height Tff7b72= Te6edf3minHeightTb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)

Tff7b72val Te6edf3bitmap Tff7b72= Te6edf3createBitmapTb4b4b4(Te6edf3widthTb4b4b4, Te6edf3heightTb4b4b4)
Tff7b72val Te6edf3canvas Tff7b72= Te6edf3CanvasTb4b4b4(Te6edf3bitmapTb4b4b4)

Tff7b72val Te6edf3bgPaint Tff7b72= Te6edf3PaintTb4b4b4(Te6edf3PaintTb4b4b4.Te6edf3ANTI_ALIAS_FLAGTb4b4b4)Tb4b4b4.Te6edf3apply Tb4b4b4{ Te6edf3color Tff7b72= Te6edf3nodeColorInt Tb4b4b4}
Tff7b72val Te6edf3cornerRadius Tff7b72= Te6edf3CHIP_CORNER_RADIUS_DP Tff7b72* Te6edf3density
Te6edf3canvasTb4b4b4.Te6edf3drawRoundRectTb4b4b4(Te6edf3RectFTb4b4b4(T79c0ff0fTb4b4b4, T79c0ff0fTb4b4b4, Te6edf3widthTb4b4b4.Te6edf3toFloatTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3heightTb4b4b4.Te6edf3toFloatTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4, Te6edf3cornerRadiusTb4b4b4, Te6edf3cornerRadiusTb4b4b4, Te6edf3bgPaintTb4b4b4)

Tff7b72val Te6edf3textX Tff7b72= Te6edf3width Tff7b72/ T79c0ff2f
Tff7b72val Te6edf3textY Tff7b72= Tb4b4b4(Te6edf3height Tff7b72/ T79c0ff2fTb4b4b4) Tff7b72- Tb4b4b4(Tb4b4b4(Te6edf3textPaintTb4b4b4.Te6edf3descentTb4b4b4(Tb4b4b4) Tff7b72+ Te6edf3textPaintTb4b4b4.Te6edf3ascentTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72/ T79c0ff2fTb4b4b4)
Te6edf3canvasTb4b4b4.Te6edf3drawTextTb4b4b4(Te6edf3labelTb4b4b4, Te6edf3textXTb4b4b4, Te6edf3textYTb4b4b4, Te6edf3textPaintTb4b4b4)

Tff7b72return Te6edf3BitmapDescriptorFactoryTb4b4b4.Te6edf3fromBitmapTb4b4b4(Te6edf3bitmapTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffrenderEmojiBitmapTb4b4b4(Te6edf3codePointTb4b4b4: Tffa657IntTb4b4b4, Te6edf3densityTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3fontScaleTb4b4b4: Tffa657FloatTb4b4b4)Tb4b4b4: Te6edf3BitmapDescriptor Tb4b4b4{
Tff7b72val Te6edf3scaledDensity Tff7b72= Te6edf3density Tff7b72* Te6edf3fontScale
Tff7b72val Te6edf3textPaint Tff7b72=
Te6edf3TextPaintTb4b4b4(Te6edf3PaintTb4b4b4.Te6edf3ANTI_ALIAS_FLAGTb4b4b4)Tb4b4b4.Te6edf3apply Tb4b4b4{
Te6edf3textSize Tff7b72= Te6edf3EMOJI_TEXT_SIZE_SP Tff7b72* Te6edf3scaledDensity
Te6edf3textAlign Tff7b72= Te6edf3PaintTb4b4b4.Te6edf3AlignTb4b4b4.Te6edf3CENTER
Tb4b4b4}
Tff7b72val Te6edf3emoji Tff7b72= Tffa657StringTb4b4b4(Te6edf3CharacterTb4b4b4.Te6edf3toCharsTb4b4b4(Te6edf3codePointTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3padding Tff7b72= Te6edf3EMOJI_PADDING_DP Tff7b72* Te6edf3density

Tff7b72val Te6edf3textWidth Tff7b72= Te6edf3textPaintTb4b4b4.Te6edf3measureTextTb4b4b4(Te6edf3emojiTb4b4b4)
Tff7b72val Te6edf3metrics Tff7b72= Te6edf3textPaintTb4b4b4.Te6edf3fontMetrics
Tff7b72val Te6edf3textHeight Tff7b72= Te6edf3metricsTb4b4b4.Te6edf3descent Tff7b72- Te6edf3metricsTb4b4b4.Te6edf3ascent

Tff7b72val Te6edf3width Tff7b72= Tb4b4b4(Te6edf3textWidth Tff7b72+ Te6edf3padding Tff7b72* T79c0ff2Tb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3coerceAtLeastTb4b4b4(T79c0ff1Tb4b4b4)
Tff7b72val Te6edf3height Tff7b72= Tb4b4b4(Te6edf3textHeight Tff7b72+ Te6edf3padding Tff7b72* T79c0ff2Tb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3coerceAtLeastTb4b4b4(T79c0ff1Tb4b4b4)

Tff7b72val Te6edf3bitmap Tff7b72= Te6edf3createBitmapTb4b4b4(Te6edf3widthTb4b4b4, Te6edf3heightTb4b4b4)
Tff7b72val Te6edf3canvas Tff7b72= Te6edf3CanvasTb4b4b4(Te6edf3bitmapTb4b4b4)

Tff7b72val Te6edf3textX Tff7b72= Te6edf3width Tff7b72/ T79c0ff2f
Tff7b72val Te6edf3textY Tff7b72= Te6edf3padding Tff7b72- Te6edf3metricsTb4b4b4.Te6edf3ascent
Te6edf3canvasTb4b4b4.Te6edf3drawTextTb4b4b4(Te6edf3emojiTb4b4b4, Te6edf3textXTb4b4b4, Te6edf3textYTb4b4b4, Te6edf3textPaintTb4b4b4)

Tff7b72return Te6edf3BitmapDescriptorFactoryTb4b4b4.Te6edf3fromBitmapTb4b4b4(Te6edf3bitmapTb4b4b4)
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.06s